﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Dynamic;
using System.Xml;
using System.Web.Script.Serialization;
using Newtonsoft.Json;
using JsonExSerializer;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using FluentJson;
using FluentJson.Configuration;

namespace JsonTestConsole
{
    class Program
    {
        //Test data
        private static readonly Book TestBook = new Book { 
            Title = "Around the world in 80 days",
            Type = BookType.Roman,
            PubDate = new DateTime(1892, 1, 11),
            PagesCount = 200,
            Tags = new List<string> { "travel", "adventure" },
            Author = new Author {
                FirstName = "Jules",
                LastName = "Verne"
            }
        };

        static void Main(string[] args)
        {
			//JIT-compilation, JSON code review.
			InitTest("Precompile procedures", 1, true);
            Test("Fluent", () => FluentJsonInit().Encode(TestBook));
			Test("JavaScriptSerializer", () => new JavaScriptSerializer().Serialize(TestBook));
			Test("Newton", () => JsonConvert.SerializeObject(TestBook));
			Test("JsonEx", () => JsonExSerialize(new Serializer(typeof(Book))));
			Test("string+string", StringConcatTest);
			Test("DataContractJsonSerializer", () => DataContractSerialize(new DataContractJsonSerializer(typeof(Book))));
			
			//Test №1
			InitTest("Test 1. Init+serialize testing:", 10000, false);
        	Test("Fluent", () => FluentJsonInit().Encode(TestBook));
			Test("JavaScriptSerializer", () => new JavaScriptSerializer().Serialize(TestBook));
			Test("Newton", () => JsonConvert.SerializeObject(TestBook));
			Test("JsonEx", () => JsonExSerialize(new Serializer(typeof(Book))));
			Test("string+string", StringConcatTest);
			Test("DataContractJsonSerializer", () => DataContractSerialize(new DataContractJsonSerializer(typeof(Book))));
			
			//Test №2
			InitTest("Test 2. Split initialization tests:", 10000, false);
			var fluentSerializer = FluentJsonInit();
			Test("Fluent", () => fluentSerializer.Encode(TestBook));
			
			var javaScriptSerializer = new JavaScriptSerializer();
			Test("JavaScriptSerializer", () => javaScriptSerializer.Serialize(TestBook));
			
			var jsonexSerializer = new Serializer(typeof(Book));
			Test("JsonEx", () => JsonExSerialize(jsonexSerializer));
			
			var msDataContractSerializer = new DataContractJsonSerializer(typeof(Book));
			Test("DataContractJsonSerializer", () => DataContractSerialize(msDataContractSerializer));
			Console.ReadLine();
        }

		static bool _showJsonResult = false;
		private static int _count = 1;
		
		private static void InitTest(string name, int count, bool showJsonResult)
    	{
			if(!showJsonResult) Console.WriteLine();
    		_count = count;
			_showJsonResult = showJsonResult;
    		Console.WriteLine(name);
    	}

    	private static void Test(string name, Func<string> action)
		{
			var startTime = DateTime.Now;
			string lastResult = "empty";
			for(int i=0;i<_count;i++)
				lastResult = action();
			var endTime = DateTime.Now;
			var duration = endTime.Subtract(startTime).TotalMilliseconds;
			Console.WriteLine(name + " " + _count + " times duration (ms): " + duration);
					
			if(_showJsonResult)
			{
				Console.WriteLine("Last result is:");
				Console.WriteLine(lastResult);
				Console.WriteLine();
			}
		}
		
		private static string StringConcatTest()
		{
			var testData = Program.TestBook;
			
			string tags;
			
			if(testData.Tags != null && testData.Tags.Count > 0)
			{
				tags = testData.Tags[0];
				
				for(int i=1;i<testData.Tags.Count;i++)
					tags+=", " + tags[i];
				
				tags = ", \"Tags\":["+tags +"]";
			}
			else
			{
				tags = string.Empty;
			}
			
			
			return "{ \"Title\":\""+ testData.Title.Replace("\"","\\\"")+
				"\", \"Type\":"+(int)testData.Type +
				", \"PubDate\":\""+testData.PubDate+
				"\", \"PagesCount\":"+testData.PagesCount+
				tags+
				", \"Author\":{\"FirstName\":\"" + testData.Author.FirstName.Replace("\"","\\\"")+
				"\", \"LastName\":\"" + 	testData.Author.LastName.Replace("\"","\\\"") + "\" } }";	
		}
		
		
		static string DataContractSerialize(DataContractJsonSerializer serializer)
		{
			var ms = new MemoryStream();
			serializer.WriteObject(ms, Program.TestBook);
			return Encoding.Default.GetString(ms.GetBuffer());
		}
		
		static string JsonExSerialize (Serializer serializer)
		{
			var memoryStream = new MemoryStream();
		    serializer.Serialize(Program.TestBook,	memoryStream);
			return Encoding.Default.GetString(memoryStream.GetBuffer());
		}
		
		
		private static JsonEncoder<Book> FluentJsonInit()
		{
            return Json.EncoderFor<Book>(config => config
                .MapType<Book>(map => map
                    .AllFields()
                    .Field<DateTime>(field => field.PubDate, pubDate => pubDate
                        .EncodeAs<string>(value => value.ToShortDateString()))
                    .Field<BookType>(field => field.Type, type => type
                        .EncodeAs<int>(value => (int)value)
                        .To("book_type")))
                .MapType<Author>(map => map.AllFields())
				.UseTidy(true) );
		}
    }

	//Test classes
    enum BookType
    {
        Novel, Roman, Poetry
    }
	
	[DataContract]
    class Book
    {
		[DataMember]
        public string Title { get; set; }
		[DataMember]
        public BookType Type { get; set; }
		[DataMember(Name = "PublicDate")]
        public DateTime PubDate { get; set; }
		[DataMember]
        public int PagesCount { get; set; }
		[DataMember]
        public IList<string> Tags { get; set; }

		[DataMember]
        public Author Author { get; set; }
    }

	[DataContract]
    class Author
    {
		[DataMember]
        public string FirstName { get; set; }
		[DataMember]
        public string LastName { get; set; }
    }
	
	
}
